#include "config.h"
#include "log.h"
#include <yaml-cpp/yaml.h>

yuechuan::ConfigVar<int>::ptr config_int = yuechuan::Config::Lookup("system.port", (int) 8080, "system port");
// yuechuan::ConfigVar<float>::ptr config_intx = yuechuan::Config::Lookup("system.port", (float)8080, "system port");
yuechuan::ConfigVar<float>::ptr config_float = yuechuan::Config::Lookup("system.value", (float) 10.2f, "system value");
yuechuan::ConfigVar<std::vector<int>>::ptr config_vec = yuechuan::Config::Lookup("system.vec", std::vector<int>{1, 2},
                                                                                 "system vec");
yuechuan::ConfigVar<std::list<int>>::ptr config_list = yuechuan::Config::Lookup("system.list", std::list<int>{100, 200},
                                                                                "system list");
yuechuan::ConfigVar<std::set<int>>::ptr config_set = yuechuan::Config::Lookup("system.set", std::set<int>{000, 111},
                                                                              "system set");
yuechuan::ConfigVar<std::unordered_set<int>>::ptr config_uset = yuechuan::Config::Lookup("system.uset",
                                                                                         std::unordered_set<int>{10,
                                                                                                                 20},
                                                                                         "system unordered_set");
yuechuan::ConfigVar<std::map<std::string, int>>::ptr config_map = yuechuan::Config::Lookup("system.map",
                                                                                           std::map<std::string, int>{{"key",  100},
                                                                                                                      {"key2", 300},
                                                                                                                      {"key3", 400}},
                                                                                           "system map");
yuechuan::ConfigVar<std::unordered_map<std::string, int>>::ptr config_umap = yuechuan::Config::Lookup("system.umap",
                                                                                                      std::unordered_map<std::string, int>{{"key",  200},
                                                                                                                                           {"key2", 300},
                                                                                                                                           {"key3", 500}},
                                                                                                      "system unorder_map");

yuechuan::ConfigVar<yuechuan::Person>::ptr config_class = yuechuan::Config::Lookup("class.person", yuechuan::Person(),
                                                                                   "system person");
yuechuan::ConfigVar<std::map<std::string, yuechuan::Person>>::ptr config_person_map = yuechuan::Config::Lookup(
        "class.map", std::map<std::string, yuechuan::Person>(), "system person");

void print_yaml(const YAML::Node &node, int level) {
    if (node.IsScalar()) {
        std::cout << std::string(level * 4, ' ')
                  << node.Scalar()
                  << " - "
                  << node.Type()
                  << " - "
                  << level << std::endl;
    } else if (node.IsNull()) {
        std::cout << std::string(level * 4, ' ')
                  << " Null "
                  << " - "
                  << " Null "
                  << " - "
                  << level << std::endl;
    } else if (node.IsMap()) {
        for (auto it = node.begin(); it != node.end(); ++it) {
            std::cout << std::string(level * 4, ' ')
                      << it->first
                      << " - "
                      << it->second.Scalar()
                      << " - "
                      << it->second.Type()
                      << " - "
                      << level << std::endl;
            print_yaml(it->second, level + 1);
        }
    } else if (node.IsSequence()) {
        for (size_t i = 0; i < node.size(); ++i) {
            std::cout << std::string(level * 4, ' ')
                      << node[i].Scalar()
                      << " - "
                      << node[i].Type()
                      << " - "
                      << level << std::endl;
            print_yaml(node[i], i + 1);
        }
    } else {
        std::cout << "*********";
    }
}

void test_yaml() {
    YAML::Node root = YAML::LoadFile("/home/yuechuan/Project/CProject/Yuechuan/bin/conf/log.yml");
    // std::cout << root;
    print_yaml(root, 0);
}

void test_config() {
#define XX(config_var, name, prefix)                                                                     \
    {                                                                                                    \
        auto &v = config_var->getValue();                                                                \
        for (auto &i : v)                                                                                \
            YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << #prefix " " #name " : " << i;                      \
        YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << #prefix " " #name " yaml: " << config_var->toString(); \
    }
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "before: " << config_int->getValue();
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "before: " << config_float->getValue();
#define XX_M(config_var, name, prefix)                                                                   \
    {                                                                                                    \
        auto &v = config_var->getValue();                                                                \
        for (auto &i : v)                                                                                \
            YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT())                                                       \
                << #prefix " " #name " : {" << i.first << " - " << i.second << "}";                      \
        YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << #prefix " " #name " yaml: " << config_var->toString(); \
    }
    XX(config_vec, int_vec, before);
    XX(config_list, int_list, before);
    XX(config_set, int_set, before);
    XX(config_uset, int_uset, before);
    XX_M(config_map, str_map, before);
    XX_M(config_umap, str_umap, before);

    YAML::Node root = YAML::LoadFile("/home/yuechuan/Project/CProject/Yuechuan/bin/conf/log.yml");
    yuechuan::Config::LoadFromYaml(root);
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "after: " << config_int->getValue();
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "after: " << config_float->getValue();

    XX(config_vec, int_vec, after);
    XX(config_list, int_list, after);
    XX(config_set, int_set, after);
    XX(config_uset, int_uset, after);
    XX_M(config_map, str_map, after);
    XX_M(config_umap, str_umap, after);

#undef XX
}

void test_class() {
#define XX_PM(g_var, prefix)                                                                                     \
    {                                                                                                            \
        auto m = config_person_map->getValue();                                                                  \
        for (auto &i : m)                                                                                        \
        {                                                                                                        \
            YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << prefix << ": " << i.first << " - " << i.second.toString(); \
        }                                                                                                        \
        YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << prefix << ": size=" << m.size();                               \
    }
    config_class->addListener([](const yuechuan::Person &old_value, const yuechuan::Person &new_value) {
        YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << " old_value = " << old_value.toString()
                                               << " new_value = " << new_value.toString();
    });
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "before: " << config_class->getValue().toString();
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "before: " << config_person_map->toString();

    YAML::Node root = YAML::LoadFile("/home/yuechuan/Project/CProject/Yuechuan/bin/conf/test.yml");
    yuechuan::Config::LoadFromYaml(root);
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "after: " << config_class->getValue().toString() << " - "
                                           << config_class->toString();
    XX_PM(config_person_map, "class.map after");
    YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << "after: " << config_person_map->toString();
}

void test_log() {
    static yuechuan::Logger::ptr system_log = YUECHUAN_LOG_NAME("system");
    YUECHUAN_LOG_INFO(system_log) << " hello system" << std::endl;
    std::cout << yuechuan::LoggerMgr::GetInstance()->toYamlString() << std::endl;
    YAML::Node root = YAML::LoadFile("/home/yuechuan/Project/CProject/Yuechuan/bin/conf/log.yml");
    yuechuan::Config::LoadFromYaml(root);
    std::cout << "--------------------" << std::endl;
    std::cout << yuechuan::LoggerMgr::GetInstance()->toYamlString() << std::endl;
    std::cout << "--------------------" << std::endl;
    std::cout << root << std::endl;
    YUECHUAN_LOG_INFO(system_log) << " hello system " << std::endl;
}

int main(int argc, char **argv) {
    std::cout << " here" << std::endl;
    // test_yaml();
    // test_config();
    // test_class();
    test_log();
    yuechuan::Config::Visit([](yuechuan::ConfigVarBase::ptr var) {
        YUECHUAN_LOG_INFO(YUECHUAN_LOG_ROOT()) << " name=" << var->getName()
                                               << " description=" << var->getDescription()
                                               << " typeName=" << var->getTypeName()
                                               << " value=" << var->toString();
    });

    return 0;
}